#include "gkt_board.h"

/* system systick rate */
#if defined(GKT_CONFIG_OS_ENABLE) && GKT_CONFIG_OS_ENABLE

#define SYSTICK_HZ	GKT_CONFIG_OS_TICK_RATE
extern volatile uint32_t g_systick_count;
#define systick_init()	do {} while(0)

#else

#ifdef GKT_CONFIG_SYSTICK_HZ
#define SYSTICK_HZ	GKT_CONFIG_SYSTICK_HZ
#else
#define SYSTICK_HZ	100
#endif

volatile uint32_t g_systick_count = 0;

__STATIC_INLINE void systick_init(void)
{
	/* Make SysTick the lowest priority interrupts. */
	NVIC_SetPriority(SysTick_IRQn, GKT_IRQ_PRIORITY_TO_REGVAL(GKT_IRQ_PRIORITY_KERNEL));

	SysTick->LOAD = (GKT_SYSTEM_CLOCK_HZ / SYSTICK_HZ) - 1;
	SysTick->CTRL = (1U << SysTick_CTRL_CLKSOURCE_Pos)	/* processor clock */
					| (1U << SysTick_CTRL_TICKINT_Pos)	/* counting down to zero asserts the SysTick exception request */
					| (1U << SysTick_CTRL_ENABLE_Pos);	/* counter enabled */
}

void SysTick_Handler(void)
{
	uint32_t csr;

	__set_BASEPRI(GKT_IRQ_PRIORITY_TO_REGVAL(GKT_IRQ_PRIORITY_SYSCALL));

	/* Clear overflow flag */
 	csr = SysTick->CTRL;
	if (csr & 0x10000)
		g_systick_count++;

	__set_BASEPRI(0);
}

#endif


#define SYSTEM_CLOCK_M	(GKT_SYSTEM_CLOCK_HZ / 1000000)
#define SYSTEM_CLOCK_K	(GKT_SYSTEM_CLOCK_HZ / 1000)

__SRAM_TEXT uint64_t gkt_systime_ns(void)
{
	uint64_t time;
	uint32_t csr, ticks, clocks;

	gkt_irq_global_disable();
	csr = SysTick->CTRL;
	clocks = (GKT_SYSTEM_CLOCK_HZ / SYSTICK_HZ) - SysTick->VAL;

	if (csr & 0x10000)
		g_systick_count++;
	ticks = g_systick_count;
	gkt_irq_global_enable();

	time = (uint64_t)(clocks * 1000) / SYSTEM_CLOCK_M;
	time += (uint64_t)ticks * (1000000000 / SYSTICK_HZ);

	return time;
}

__SRAM_TEXT uint64_t gkt_systime_us(void)
{
	uint64_t time;
	uint32_t csr, ticks, clocks;

	gkt_irq_global_disable();
	csr = SysTick->CTRL;
	clocks = (GKT_SYSTEM_CLOCK_HZ / SYSTICK_HZ) - SysTick->VAL;

	if (csr & 0x10000)
		g_systick_count++;
	ticks = g_systick_count;
	gkt_irq_global_enable();

	time = (uint64_t)clocks / SYSTEM_CLOCK_M;
	time += (uint64_t)ticks * (1000000 / SYSTICK_HZ);

	return time;
}

__SRAM_TEXT uint32_t gkt_systime_ms(void)
{
	uint32_t csr, ticks, clocks;
	uint32_t time;

	gkt_irq_global_disable();
	csr = SysTick->CTRL;
	clocks = (GKT_SYSTEM_CLOCK_HZ / SYSTICK_HZ) - SysTick->VAL;

	if (csr & 0x10000)
		g_systick_count++;
	ticks = g_systick_count;
	gkt_irq_global_enable();

	time = (uint64_t)clocks / SYSTEM_CLOCK_K;
	time += (uint64_t)ticks * (1000 / SYSTICK_HZ);

	return time;
}

__SRAM_TEXT void gkt_delay_us(uint32_t time_us)
{
	uint64_t time_end = gkt_systime_ns() + (uint64_t)(time_us * 1000) + 1;
	while (gkt_systime_ns() < time_end);
}

__SRAM_TEXT void gkt_delay_ms(uint32_t time_ms)
{
	uint64_t time_end = gkt_systime_us() + (time_ms * 1000) + 1;
	while (gkt_systime_us() < time_end);
}

void gkt_systime_init(void)
{
	systick_init();
}

